package org.dftproject.patml.data;

import org.dftproject.genesis.core.persistence.IMemento;
import org.dftproject.genesis.core.persistence.IPersistable;
import org.dftproject.genesis.data.genealogy.Events;
import org.dftproject.genesis.data.genealogy.GenealogyConstants;
import org.dftproject.genesis.data.genealogy.IEvent;
import org.dftproject.genesis.data.genealogy.IPerson;
import org.dftproject.genesis.data.genealogy.IRole;
import org.dftproject.genesis.data.genealogy.Sex;
import org.dftproject.genesis.data.genealogy.impl.EventImpl;
import org.dftproject.genesis.data.genealogy.impl.InstantImpl;
import org.dftproject.genesis.data.genealogy.impl.NameImpl;
import org.dftproject.genesis.data.genealogy.impl.PlaceImpl;
import org.dftproject.genesis.data.genealogy.impl.RoleImpl;
import org.dftproject.genesis.data.genealogy.impl.SourceImpl;
import org.dftproject.genesis.data.impl.AttributeImpl;
import org.dftproject.patml.XmlUtils;
import org.dftproject.patml.factories.PatmlPersonFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class PatmlPerson extends LazyPerson implements IPersistable {

	private final PatmlSite site;
	private final String id;

	public PatmlPerson(PatmlSite site, String id) {
		this.site = site;
		this.id = id;
	}

	public PatmlSite getSite() {
		return site;
	}

	public String getId() {
		return id;
	}

	@Override
	protected void init() {
		Document data = site.getPersonData(id);
		if (data == null)
			return;
		
		Node root = null;
		NodeList persons = data.getDocumentElement().getElementsByTagName("Person");
		if (persons != null && persons.getLength() > 0)
			root = persons.item(0);
		if (root == null)
			return;
		
		SourceImpl source = new SourceImpl();
		source.addAttribute(new AttributeImpl(GenealogyConstants.type, "org.dftproject.patml.source"));
		source.addAttribute(new AttributeImpl("site", site));
		source.addAttribute(new AttributeImpl("id", id));

		AttributeImpl attribute = new AttributeImpl(GenealogyConstants.name, new NameImpl(XmlUtils.getString(root, "Name/text()").trim()));
		attribute.addAttribute(new AttributeImpl(GenealogyConstants.source, source));
		addAttribute(attribute);
		
		String gender = XmlUtils.getString(root, "Gender/text()").trim();
		if ("Male".equalsIgnoreCase(gender)) {
			attribute = new AttributeImpl(GenealogyConstants.sex, Sex.Male);
			attribute.addAttribute(new AttributeImpl(GenealogyConstants.source, source));
			addAttribute(attribute);
		} else if ("Female".equalsIgnoreCase(gender)) {
			attribute = new AttributeImpl(GenealogyConstants.sex, Sex.Female);
			attribute.addAttribute(new AttributeImpl(GenealogyConstants.source, source));
			addAttribute(attribute);
		}
		
		String birthDate = XmlUtils.getString(root, "Birth/Date/text()").trim();
		if ("".equals(birthDate))
			birthDate = null;
		String birthPlace = XmlUtils.getString(root, "Birth/Location/text()").trim();
		if ("".equals(birthPlace))
			birthPlace = null;
		
		IPerson father = null;
		String fatherId = site.getPersonId(XmlUtils.getString(root, "Birth/Parents/Father/Identifier/text()").trim());
		if (fatherId != null)
			father = new PatmlPerson(site, fatherId);
		
		IPerson mother = null;
		String motherId = site.getPersonId(XmlUtils.getString(root, "Birth/Parents/Mother/Identifier/text()").trim());
		if (motherId != null)
			mother = new PatmlPerson(site, motherId);
		
		if (birthDate != null || birthPlace != null || father != null || mother != null) {
			EventImpl birthEvent = new EventImpl();
			
			if (birthDate != null)
				birthEvent.addAttribute(new AttributeImpl(Events.instant, new InstantImpl(birthDate)));
			if (birthPlace != null)
				birthEvent.addAttribute(new AttributeImpl(Events.place, new PlaceImpl(birthPlace)));
			
			addRole(new RoleImpl(GenealogyConstants.Child, this, birthEvent));
			if (father != null)
				birthEvent.addRole(new RoleImpl(GenealogyConstants.Father, father, birthEvent));
			if (mother != null)
				birthEvent.addRole(new RoleImpl(GenealogyConstants.Mother, mother, birthEvent));
		}
		
		String deathDate = XmlUtils.getString(root, "Death/Date/text()").trim();
		if ("".equals(deathDate))
			deathDate = null;
		String deathPlace = XmlUtils.getString(root, "Death/Location/text()").trim();
		if ("".equals(deathPlace))
			deathPlace = null;
		
		if (deathDate != null || deathPlace != null || father != null || mother != null) {
			EventImpl deathEvent = new EventImpl();
			
			if (deathDate != null)
				deathEvent.addAttribute(new AttributeImpl(Events.instant, new InstantImpl(deathDate)));
			if (deathPlace != null)
				deathEvent.addAttribute(new AttributeImpl(Events.place, new PlaceImpl(deathPlace)));
			
			addRole(new RoleImpl(GenealogyConstants.Deceased, this, deathEvent));
		}
		
		NodeList spouseIds = XmlUtils.getNodeList(root, "Marriage/Spouse/Identifier/text()");
		int spouseCount = spouseIds.getLength();
		for (int i = 0; i < spouseCount; i++) {
			PatmlPerson spouse = null;
			String spouseId = site.getPersonId(spouseIds.item(i).getNodeValue().trim());
			if (spouseId != null) {
				spouse = new PatmlPerson(site, spouseId);
				
				EventImpl marriageEvent = new EventImpl();
				addRole(new RoleImpl(GenealogyConstants.Spouse, this, marriageEvent));
				marriageEvent.addRole(new RoleImpl(GenealogyConstants.Spouse, this, marriageEvent));
				marriageEvent.addRole(new RoleImpl(GenealogyConstants.Spouse, spouse, marriageEvent));
			}
			
			NodeList childrenIds = XmlUtils.getNodeList(root, "Marriage/Child/Identifier/text()");
			int childrenCount = childrenIds.getLength();
			for (int j = 0; j < childrenCount; j++) {
				IPerson child = null;
				String childId = site.getPersonId(childrenIds.item(j).getNodeValue().trim());
				if (childId != null) {
					child = new PatmlPerson(site, childId);
					for (IRole childRole : child.getRoles(GenealogyConstants.Child)) {
						IEvent birthEvent = childRole.getEvent();
						addRole(new RoleImpl(GenealogyConstants.Parent, this, birthEvent));
					}
				}
			}
		}
	}

	public String getFactoryId() {
		return PatmlPersonFactory.ID;
	}

	public void saveState(IMemento memento) {
		IMemento child = memento.createChild("site");
		site.saveState(child);
		memento.putString("siteFactoryId", site.getFactoryId());
		memento.putString("id", id);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((site == null) ? 0 : site.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final PatmlPerson other = (PatmlPerson) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (site == null) {
			if (other.site != null)
				return false;
		} else if (!site.equals(other.site))
			return false;
		return true;
	}

}
